﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Antlr3.ST;
using System.IO;
using System.Reflection;

namespace com.guysherman.simulclass
{
    public class ScannedMethod : ScannedMember
    {
        public MethodInfo MemberMethodInfo { get; set; }


        private string MethodName
        {
            get
            {
                return string.Format("{0}{1}", nameDecoratorForVisibility(this.Visibility), this.Name);
            }
        }

        public override string MemberFunctionDeclaration()
        {
            StringBuilder declaration = new StringBuilder();

            StringTemplateGroup group = new StringTemplateGroup(new StringReader(Templates.HeaderTemplate));
            StringTemplate t = group.GetInstanceOf("memberFunctionDeclaration");

            
            t.SetAttribute("decorators", this.MemberType.EmbeddedTypeName());
            t.SetAttribute("name", MethodName);


            ParameterInfo[] parameters = MemberMethodInfo.GetParameters();

            string arg = "MonoObject* target";
            t.SetAttribute("args", arg);

            foreach (ParameterInfo param in parameters)
            {
                arg = argument(param);
                t.SetAttribute("args", arg);
            }

            
            
            
            declaration.AppendLine(t.ToString());
            
            return declaration.ToString();
        }

        private string argument(ParameterInfo param)
        {
            return string.Format("{0} {1}", param.ParameterType.EmbeddedTypeName(), param.Name);
        }

        public override string MemberVariableDeclaration()
        {
            return string.Format("MonoMethod* {0}MonoMethod;", this.Name);
        }

        public override string MemberFunctionDefinition()
        {
            StringBuilder definition = new StringBuilder();

            StringTemplateGroup group = new StringTemplateGroup(new StringReader(Templates.HeaderTemplate));

            

            ParameterInfo[] parameters = MemberMethodInfo.GetParameters();

            StringTemplate methodTemplate = group.GetInstanceOf("memberFunctionDefinition");
            methodTemplate.SetAttribute("argCount", parameters.Length);
            methodTemplate.SetAttribute("argsArray", parameters.Length > 0 ? string.Format("void* args[{0}];", parameters.Length) : "void** args = NULL;");
            methodTemplate.SetAttribute("className", this.ClassName);
            methodTemplate.SetAttribute("methodName", MethodName);
            methodTemplate.SetAttribute("returnType", this.MemberType.EmbeddedTypeName());
            methodTemplate.SetAttribute("memberName", this.Name);

            string arg = "MonoObject* target";
            methodTemplate.SetAttribute("args", arg);

            for (int i = 0; i < parameters.Length; i++)
            {
                ParameterInfo param = parameters[i];
                arg = argument(param);
                methodTemplate.SetAttribute("args", arg);

                string packedArgTemplateName = string.Empty;
                string unboxedArgTemplateName = string.Empty;
                if (param.ParameterType.IsByRef)
                {
                    if (param.ParameterType.FullName == "System.String&")
                    {
                        packedArgTemplateName = "packedByRefStringArg";
                        unboxedArgTemplateName = "unboxedStringByRefArg";

                    }
                    else if (param.ParameterType.IsPrimitive)
                    {
                        packedArgTemplateName = "packedByRefValArg";
                        unboxedArgTemplateName = "unboxedByRefValArg";
                    }
                    else
                    {
                        packedArgTemplateName = "packedByRefRefArg";
                        unboxedArgTemplateName = "unboxedRefByRefArg";
                    }
                }
                else
                {
                    if (param.ParameterType.FullName == "System.String")
                    {
                        packedArgTemplateName = "packedStringArg";
                    }
                    else if (param.ParameterType.IsPrimitive)
                    {
                        packedArgTemplateName = "packedValArg";
                    }
                    else
                    {
                        packedArgTemplateName = "packedRefArg";
                    }
                }


                StringTemplate packedArgTemplate = group.GetInstanceOf(packedArgTemplateName);
                packedArgTemplate.SetAttribute("argName", param.Name);
                packedArgTemplate.SetAttribute("argIndex", i);
                packedArgTemplate.SetAttribute("argType", param.ParameterType.EmbeddedTypeName());
                packedArgTemplate.SetAttribute("lowerCsClass", param.ParameterType.Name.ToLower().Substring(0, param.ParameterType.Name.Length -1));
                methodTemplate.SetAttribute("packedArgs", packedArgTemplate.ToString());

                if (param.ParameterType.IsByRef)
                {
                    StringTemplate unboxedArgTemplate = group.GetInstanceOf(unboxedArgTemplateName);
                    unboxedArgTemplate.SetAttribute("argName", param.Name);
                    unboxedArgTemplate.SetAttribute("type", param.ParameterType.EmbeddedTypeName().Substring(0, param.ParameterType.EmbeddedTypeName().Length - 1) );
                    methodTemplate.SetAttribute("unpackedArgs", unboxedArgTemplate.ToString());
                }


                
            }

            string unboxedReturnTemplateName = string.Empty;

            if (MemberType.FullName == "System.String")
            {
                unboxedReturnTemplateName = "unboxedStringReturn";
            }
            else if (MemberType.IsPrimitive)
            {
                unboxedReturnTemplateName = "unboxedValReturn";
            }
            else if (MemberType.FullName == "System.Void")
            {
                unboxedReturnTemplateName = "unboxedVoidReturn";
            }
            else
            {
                unboxedReturnTemplateName = "unboxedRefReturn";
            }

            StringTemplate unboxedReturn = group.GetInstanceOf(unboxedReturnTemplateName);
            unboxedReturn.SetAttribute("type", this.MemberType.EmbeddedTypeName());
            methodTemplate.SetAttribute("unboxedReturn", unboxedReturn.ToString());


            definition.AppendLine(methodTemplate.ToString());

            return definition.ToString();
        }
    }
}
